<!--甘特图首页-->
<template>
  <div class="gantt-table">
    <!-- 头部-->
    <!-- <div class="gantt-head">
			<div class="gantt-fixed">
				<div class="gantt-head" style="padding: 5px 20px;">
					<span>时间区间:</span>
					<div style="width: 240px;">{{ _formatDate(choiceTime[0]) }} - {{ _formatDate(choiceTime[1]) }}</div>
					 <el-button type="primary" size="mini" @click="save">保存</el-button>

					<el-button type="danger" size="mini" plain @click="_hisBack">撤回({{ hisList.length }})</el-button>

					<el-button type="danger" v-if="!isDeleteMode" size="mini" plain @click="isDeleteMode = true">批量删除</el-button>
					<el-button type="danger" v-if="isDeleteMode" size="mini" plain @click="isDeleteMode = false">取消删除</el-button> 
				</div>
			</div>
		</div> -->
    <div class="gantt-box">
      <div class="left" ref="left">
        <div class="head">
          <!--    左边栏的列表头      -->
          <div class="head-box">
            <GanttRow>
              <!-- <GanttBlock>置顶</GanttBlock> -->
              <!-- <GanttBlock>类型</GanttBlock> -->
              <GanttBlock class="text-line-1" style="width: 800px"
                >资源/设备编号</GanttBlock
              >
              <GanttBlock>资源名称</GanttBlock>
              <GanttBlock>班次</GanttBlock>
            </GanttRow>
          </div>
        </div>
        <!--  左边栏的列表内容      -->
        <div class="left-box" :style="{ transform: `translateY(-${transFormYValue}px)` }">
          <GanttRow v-for="(item, index) in ganttData" :key="index">
            <!-- <GanttBlock :style="_mergeStyle('typeName', item.typeName, index)">{{ item.typeName }}</GanttBlock> -->
            <GanttBlock class="text-line-1" style="width: 800px"
              >{{ item.resourceName }}
            </GanttBlock>
            <GanttBlock>{{ item.workCenter }}</GanttBlock>
            <GanttBlock>{{ item.workCalendarName }}</GanttBlock>
          </GanttRow>
        </div>
      </div>
      <!--    右边的内容      -->
      <div class="right" ref="right">
        <!--  时间头      -->
        <div class="head">
          <div class="head-box">
            <GanttRow
              :style="{ transform: `translatex(-${transFormXValue}px)` }"
              class="transition-transForm"
            >
              <GanttTimeBlock
                :target-time="_formatDate(item)"
                v-for="(item, index) in choiceTimeArr"
                :key="index"
              ></GanttTimeBlock>
            </GanttRow>
          </div>
        </div>
        <div class="content" ref="contentGrid">
          <!--      甘特图中具体的内容   -->
          <div class="gantt-grid" ref="box" @contextmenu.prevent="() => {}">
            <!--   网格状图表  -->
            <GanttRow
              v-for="(item, rowIndex) in ganttData"
              :key="rowIndex"
              :id="`ganttRow-${rowIndex}`"
              class="gantt-row-relative"
              :class="{ 'gantt-row-active': nowSuck === rowIndex }"
            >
              <!-- <template>  -->
              <div
                v-for="(times, index) in rowBlocks(item[listKey])"
                :key="`times-${index}`"
                :style="_timeBlockStyle(times)"
                class="time-block gantt-grab gantt-grab-no"
                :class="' block-type-' + Math.round(Math.random() * 100)"
                @mousedown="onMouseDown($event, times.id, rowIndex)"
                @contextmenu.prevent="onRightClick($event, item[listKey], times)"
                @click.stop="onClicked(times, index)"
                :title="times.resourceName"
                @dblclick="onDbClicked(times, index)"
              >
                {{ times.resourceName }}
              </div>
              <!-- </template>	 -->
              <!--      block => 180  因为[this.listKey] => 60    60*3 = 180px   -->
              <GanttBlock
                class="time"
                v-for="rowIndex in choiceTimeArr.length"
                :key="rowIndex"
                style="flex-shrink: 0"
              ></GanttBlock>
            </GanttRow>
          </div>
          <div class="ant-table-content">
            <table
              style="
                width: 1000px;
                min-width: 100%;
                table-layout: fixed;
                text-align: center;
              "
            >
              <thead class="ant-table-thead">
                <tr>
                  <th class="ant-table-cell ant-table-cell-ellipsis">
                    <span>工单编号</span>
                  </th>
                  <th class="ant-table-cell ant-table-cell-ellipsis">
                    <span>产品名称</span>
                  </th>
                  <th class="ant-table-cell ant-table-cell-ellipsis">
                    <span>工序号</span>
                  </th>
                  <th class="ant-table-cell ant-table-cell-ellipsis">
                    <span>生产数量</span>
                  </th>
                  <th class="ant-table-cell ant-table-cell-ellipsis">
                    <span>上线时间</span>
                  </th>
                  <th class="ant-table-cell ant-table-cell-ellipsis" style="width: 200px">
                    <span>计划开工</span>
                  </th>
                  <th class="ant-table-cell ant-table-cell-ellipsis" style="width: 200px">
                    <span>计划完工</span>
                  </th>
                </tr>
              </thead>
              <tbody class="ant-table-tbody">
                <tr v-for="(item, index) in tableData" :key="index">
                  <td class="ant-table-cell ant-table-cell-ellipsis">
                    {{ item.orderNumber }}
                  </td>
                  <td class="ant-table-cell ant-table-cell-ellipsis">
                    {{ item.productName }}
                  </td>
                  <td class="ant-table-cell ant-table-cell-ellipsis">
                    {{ item.productCode }}
                  </td>
                  <td class="ant-table-cell ant-table-cell-ellipsis">
                    {{ item.quantity }}
                  </td>
                  <td class="ant-table-cell ant-table-cell-ellipsis">
                    {{ item.schedulingTime }}
                  </td>
                  <td class="ant-table-cell ant-table-cell-ellipsis">
                    {{ item.startTime }}
                  </td>
                  <td class="ant-table-cell ant-table-cell-ellipsis">
                    {{ item.endTime }}
                  </td>
                </tr>
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </div>
    <!-- 右键菜单 -->
    <!-- 	<div id="contextmenu" v-show="false" class="menu">
			<div class="contextmenu__item">查看</div>
			<div class="contextmenu__item" @click="onEditBlock(editBlock, editRow)">修改</div>
			<div class="contextmenu__item" @click="onDeleteBlock(editBlock)">删除</div>
		</div> -->

    <!-- 编辑窗口 -->
    <!-- <el-dialog title="编辑时间块" :visible.sync="isEditBlock">
			<el-form v-if="editBlock" :model="editBlock" label-width="120px">
				<el-form-item label="名称"><el-input v-model="editBlock.projectName"
						autocomplete="off"></el-input></el-form-item>
				<el-form-item label="开始时间">
					<el-date-picker @change="onChangeStartTime" v-model="editBlock.startTime" type="datetime"
						placeholder="选择日期时间"></el-date-picker>
				</el-form-item>
				<el-form-item label="时间长度">
					<el-input-number v-model="editBlock.timeDiff" @change="onChangeStartTime" :min="1" :max="100"
						label="时间长度"></el-input-number>
				</el-form-item>
				<el-form-item label="结束时间"><el-date-picker readonly v-model="editBlock.endTime" type="datetime"
						placeholder="选择日期时间"></el-date-picker></el-form-item>
			</el-form>
			    <div slot="footer" class="dialog-footer">
				<el-button @click="onEditCancel()">取 消</el-button>
				<el-button type="primary" @click="onEditSave()">确 定</el-button>
			</div>
		</el-dialog> -->
    <GanttModel
      @register="register"
      @success="reload"
      v-bind="$attrs"
      :planid="planid"
    ></GanttModel>
  </div>
</template>

<script>
import GanttRow from "/@/components/gantt-v/gantt-row.vue";
import GanttBlock from "/@/components/gantt-v/gantt-block.vue";
import GanttTimeBlock from "/@/components/gantt-v/gantt-time-block.vue";
import GanttModel from "/@/views/components/GanttModel.vue";
import ganttMix from "/@/components/gantt-v/gantt-mix.js";
import ganttHisMix from "/@/components/gantt-v/gantt-his-mix.js";
//import airPlaneData from '/@/views/fake-data/airPlaneData.js';
import { defHttp } from "/@/utils/http/axios";
//注册model
import { useModal } from "/@/components/Modal";
export default {
  name: "gantt-index",
  components: {
    GanttBlock,
    GanttRow,
    GanttTimeBlock,
    GanttModel,
  },
  mixins: [ganttMix, ganttHisMix],
  props: {
    gttParams: {
      type: Object,
      default: {},
    },
    dayColumns: {
      type: Array,
      default: [],
    },
  },
  setup() {
    const [register, { openModal }] = useModal();
    return {
      register,
      openModal,
    };
  },
  data() {
    return {
      tableData: [],
      tableList: [],
      // 用right 的滚动，控制aside-left的滚动
      transFormYValue: 0,
      // 用right 的滚动，控制head-left的滚动
      transFormXValue: 0,
      // 飞机的数据，用于渲染左侧边
      ganttData: [],
      // 选择的时间区间
      choiceTime: [new Date("2023-04-01 00:00:00"), new Date("2023-04-30 00:00:00")],
      defaultTop: 4,
      nowSuck: -1,
      // X轴移动距离
      moveX: 0,
      // Y轴移动距离
      moveY: 0,

      // 是否显示编辑弹出框
      isEditBlock: false,
      // 编辑内容
      editBlock: null,
      // 编辑行
      editRow: null,
      // 数据列表key名字
      listKey: "list",
      //右键菜单
      menuVisible: false,
      planid: "",
    };
  },
  provide() {
    return {
      page: this,
    };
  },
  computed: {
    // 通过选择的时间区间，得到这区间之间的数组
    choiceTimeArr() {
      const timeArr = [];
      // 时间戳毫秒为单位
      // 尾时间-首时间 算出头尾的时间戳差  再换算成天单位                                   毫秒->分->时->天
      const diffDays =
        (this.choiceTime[1].getTime() - this.choiceTime[0].getTime()) /
        1000 /
        60 /
        60 /
        24;

      // 一天的时间戳
      const oneDayMs = 24 * 60 * 60 * 1000;
      // 差了多少天就便利多少天 首时间+当前便利的天数的毫秒数
      for (let i = 0; i < diffDays + 1; i++) {
        // 时间戳来一个相加，得到的就是时间数组
        timeArr.push(new Date(this.choiceTime[0].getTime() + i * oneDayMs));
      }

      // console.log(diffDays, oneDayMs, timeArr);
      return timeArr;
    },

    // 行列表过滤删除的时间段,已删除的不显示
    rowBlocks() {
      return (list) => {
        return list.filter((item) => {
          return item.isValidate === "1";
        });
      };
    },
  },
  created() {
    //this.tableList = this.tableData;
  },
  watch: {
    gttParams: {
      handler(newvalue, oldvalue) {
        this.getPlansDayAllDetalis();
      },
      deep: true,
    },
  },
  methods: {
    reload() {
      // console.log("reload")
    },
    // 滚动事件
    getScrollEvent() {
      const rightDom = this.$refs.contentGrid;
      rightDom.addEventListener("scroll", (e) => {
        const scrollLeft = e.target.scrollLeft;
        const scrollTop = e.target.scrollTop;
        this.transFormYValue = scrollTop;
        this.transFormXValue = scrollLeft;
      });

      const leftDom = this.$refs.left;
      leftDom.addEventListener("mousewheel", (e) => {
        const wheelValue = e.deltaY;
        rightDom.scrollTop += wheelValue;
      });
    },

    /**
     * 当对于这个时间块按下的时候
     * @param e 时间块的 event 事件
     * @param blockId 时间块的ID
     * @param rowIndex 该条线的id
     */
    onMouseDown(e, blockId, rowIndex) {
      // 删除模式下不处理拖动事件
      if (this.isDeleteMode) {
        return;
      }
      this.moveX = 0;
      this.moveY = 0;

      // 用box 移动，不采用 Doucment
      const box = this.$refs.box;
      const dom = e.target;
      // 算出鼠标相对元素的位置
      const disX = e.clientX - dom.offsetLeft;
      const disY = e.clientY - dom.offsetTop;

      // 当点击下来的时候 nowSuck 其实等于的就是当前index
      this.nowSuck = rowIndex;

      // 让本来拥有手掌样式的class取消
      dom.classList.remove("gantt-grab");
      // 让整个box 鼠标都是抓住
      box.classList.add("gantt-grabbing");

      // 如果事件绑定在 dom上，那么一旦鼠标移动过快就会脱离掌控
      box.onmousemove = (ee) => {
        // 获得当前受到拖拽的div 用于计算suck 所谓拖引的行数
        const top = parseInt(dom.style.top);
        // 四舍五入 获得磁性吸附激活的值 (索引值)  60是block的height 10是时间块距离block的top  suck 可以当作row的索引
        let suck = Math.round((top - 10) / 60) + rowIndex;

        // suck的边界值设置
        if (suck < 0) {
          suck = 0;
        } else if (suck > this.ganttData.length - 1) {
          suck = this.ganttData.length - 1;
        }

        // suck 行样式变化
        this.nowSuck = suck;

        // 移动事件
        this.onMouseMove(ee, disX, disY, dom);
      };
      // 不管在哪里，鼠标松开的时候，清空事件 所以对于这个 “不管在哪里，使用了window”
      window.onmouseup = () => {
        // 鼠标松开了，让时间块恢复手掌样式
        dom.classList.add("gantt-grab");
        // 整个box 不在抓住了，变成箭头鼠标
        box.classList.remove("gantt-grabbing");
        // 当移动距离小于5时,不做移动处理
        if (this.moveX < 1 && this.moveY < 1 && this.moveX > -1 && this.moveY > -1) {
          console.log("无效移动");
          box.onmousemove = null;
          window.onmouseup = null;
          this.nowSuck = -1;
          return;
        }
        // 保存操作日志
        this._addHisList(this.ganttData);

        const index = this.ganttData[rowIndex][this.listKey].findIndex((item) => {
          return item.id === blockId;
        });
        const oldTimeBlock = this.ganttData[rowIndex][this.listKey][index];
        // let timeId = oldTimeBlock.id;

        // startTime 与 endTime 用于数据重新替换  上面css已经经过计算 15px为1小时
        const startTime = new Date(
          this.choiceTime[0].getTime() + (parseInt(dom.style.left) * 3600000) / 15
        );
        const endTime = new Date(
          this._getTime(startTime) + (parseInt(dom.style.width) * 3600000) / 15
        );
        const suck = this.nowSuck;

        // 加入新数据
        const data = oldTimeBlock;
        //时间格式化
        let sTime = this.changeTime(startTime);
        let eTime = this.changeTime(endTime);
        // 更新开始时间和结束时间
        data.startTime = sTime;
        data.endTime = eTime;
        //修改时间块的位置信息[暂时屏蔽掉]
        data.workCenterId = this.ganttData[suck][this.listKey][index].workCenterId;
        //this.$set(data, 'startTime', startTime);
        //this.$set(data, 'endTime', endTime);
        // 修改时间块的topParentId

        //this.$set(data, 'topParentId', this.ganttData[suck].id);
        /**
         * 本来dom元素磁性吸附是打算用document.appendChild() 方式来做的，但是对于vue来说 for出来的子元素就算变了位置，其index也不属于新的row
         */
        // 老数据 删除
        this.ganttData[rowIndex][this.listKey].splice(index, 1);

        // 新数据加入
        this.ganttData[suck][this.listKey].push(data);

        // 坐标定位 磁性吸附 永远的10px   不知道为啥动态绑定的元素也会受到以前元素的影响，可能是 for 中 index带来的影响
        dom.style.top = this.defaultTop + "px";

        // 松开鼠标的时候 清空
        box.onmousemove = null;
        window.onmouseup = null;

        // 需要重新计算吸附位置,以及整行重新排列
        this.$nextTick(() => {
          this._recalcRowTimes(data, this.ganttData[suck][this.listKey]);
        });

        // 将当前row 清空
        this.nowSuck = -1;
        // 改变位置后回调事件
        this.afterChangePosition(
          data,
          this.ganttData[rowIndex].id,
          this.ganttData[suck].id
        );
      };
    },

    // 时间块点击
    onClicked(times) {
      this.getPlansDayOneDetalis(times.workCenterId);
    },

    /**
     * 鼠标移动的时候，前置条件鼠标按下
     * @param e 时间块的 event 事件
     * @param disX x轴
     * @param disY y轴
     * @param targetDom 时间块的dom 其实可以直接 e.target 获取
     */
    onMouseMove(e, disX, disY, targetDom) {
      // 用鼠标的位置减去鼠标相对元素的位置，得到元素的位置
      let left = e.clientX - disX;
      const top = e.clientY - disY;
      this.moveX = left;
      this.moveY = top;

      // 移动位置不能小于零(开始时间)
      if (left < 0) {
        left = 0;
      }

      //拖动时间块关闭右键菜单
      this.menuVisible = false;

      targetDom.style.left = left + "px";
      targetDom.style.top = top + "px";
    },
    /**
     * 时间块位置变化后回调事件
     * @param data 时间块的值 包括时间块id startTime endTime
     * @param rowOId 时间块原来所在的那个飞机id (一条横线)
     * @param rowNId 时间块新的所在的那个飞机id
     */
    afterChangePosition(data, rowOId, rowNId) {
      //保存数据
      // this.savePlansDayDetalis(data);
    },

    save() {
      console.log(JSON.stringify(this.ganttData));
    },

    // 删除时间块,需要传入时间块信息和序号
    onDeleteBlock(block) {
      // 保存操作日志
      this._addHisList(this.ganttData);
      // const row = this.ganttData[rowIndex] /
      // row[this.listKey].splice(index, 1)
      this.$set(block, "isValidate", "0");
    },

    onRightClick(event, row, block) {
      //编辑需要把时间长度先计算好
      block.timeDiff = (block.endTime - block.startTime) / 3600000;
      this.editRow = row;
      this.editBlock = block;

      this.menuVisible = false; // 先把模态框关死，目的是 第二次或者第n次右键鼠标的时候 它默认的是true
      this.menuVisible = true; // 显示模态窗口，跳出自定义菜单栏
      event.preventDefault(); //关闭浏览器右键默认事件
      this.CurrentRow = row;
      var menu = document.querySelector(".menu");
      this.styleMenu(menu);
    },

    cancelMouse() {
      // 取消鼠标监听事件 菜单栏
      this.menuVisible = false;
      document.removeEventListener("click", this.foo); // 关掉监听，
    },

    styleMenu(menu) {
      if (event.clientX > 1800) {
        menu.style.left = event.clientX - 100 + "px";
      } else {
        menu.style.left = event.clientX + 1 + "px";
      }
      document.addEventListener("click", this.cancelMouse); // 给整个document新增监听鼠标事件，点击任何位置执行foo方法
      if (event.clientY > 700) {
        menu.style.top = event.clientY - 30 + "px";
      } else {
        menu.style.top = event.clientY - 10 + "px";
      }
    },

    // 开始编辑时间块,需要传入时间块和本行列表
    onEditBlock() {
      this.editBlock = JSON.parse(JSON.stringify(this.editBlock));
      this.isEditBlock = true;
    },

    onChangeStartTime(event) {
      console.log("onChangeStartTime:", event);
      let startTime = this._getTime(this.editBlock.startTime);
      this._updateEndTimeByDiff(this.editBlock, startTime);
    },

    // 保存数据
    onEditSave() {
      this.isEditBlock = false;
      console.log(this.editRow, this.editBlock);

      const index = this.editRow.findIndex((item) => {
        return item.id === this.editBlock.id;
      });
      if (index >= 0) {
        // 保存操作日志
        this._addHisList(this.ganttData);
        this.$set(this.editRow, index, this.editBlock);
        // 需要重新计算吸附位置,以及整行重新排列
        this.$nextTick(() => {
          this._recalcRowTimes(this.editBlock, this.editRow);
        });
      }
    },

    // 取消保存
    onEditCancel() {
      this.isEditBlock = false;
    },
    //根据计划id获取全部数据
    getPlansDayAllDetalis() {
      let thisVW = this;
      defHttp
        .post({
          url: "/ps/psProductionArrangementPlansDayDetalis/all",
          params: this.gttParams.value,
        })
        .then((res) => {
          if (res && res.length > 0) {
            for (let i = 0; i < res.length; i++) {
              let dat = res[i].list;
              if (dat != null && dat.length > 0) {
                for (let j = 0; j < dat.length; j++) {
                  // 混入的方法是使用planStartTime和planEndTime去界定甘特图位置
                  dat[j].planStartTime = dat[j].startTime;
                  dat[j].planEndTime = dat[j].endTime;
                  // 混入处理时  方法采用的是projectName  故将数据中添加个projectName 避免报错
                  dat[j].projectName = dat[j].productName;
                }
              }
            }
            thisVW.ganttData = res;
          }
        });
    },
    //点击某个甘特图单元格获取数据
    getPlansDayOneDetalis(id) {
      let thisVW = this;
      //这个是接口获取数据
      defHttp
        .get({
          url: "/ps/psProductionArrangementPlansDayDetalis/list",
          params: { workCenterId: id },
        })
        .then((res) => {
          let dt = res.records;
          if (dt && dt.length > 0) {
            thisVW.tableData = dt;
          }
        });
    },
    //保存移动的甘特图
    savePlansDayDetalis(data) {
      defHttp
        .post({
          url: "/ps/psProductionArrangementPlansDayDetalis/change",
          params: data,
        })
        .then((res) => {
          console.log(res);
          // 操作成功  请求数据
          this.getPlansDayAllDetalis();
        })
        .catch((err) => {
          console.log(err);
        });
    },
    //双击事件
    onDbClicked(times) {
      this.planid = times.id;
      this.openModal(true, {
        showFooter: true,
      });
    },

    //将中国标准时间格式化
    changeTime(tm) {
      let date = new Date(tm);
      let y = date.getFullYear();
      let m = date.getMonth() + 1;
      m = m < 10 ? "0" + m : m;
      let d = date.getDate();
      d = d < 10 ? "0" + d : d;
      let h = date.getHours();
      h = h < 10 ? "0" + h : h;
      let minutes = date.getMinutes();
      minutes = minutes < 10 ? "0" + minutes : minutes;
      let s = date.getSeconds();
      s = s < 10 ? "0" + s : s;
      return y + "-" + m + "-" + d + " " + h + ":" + minutes + ":" + s;
    },
  },
  mounted() {
    this.getScrollEvent();
    //排产计划当天详情
    this.getPlansDayAllDetalis();
    // 处理甘特图时间区间
    this.choiceTime = [
      new Date(this.dayColumns[0]?.title + " 00:00:00"),
      new Date(this.dayColumns[this.dayColumns.length - 1]?.title + " 00:00:00"),
    ];
  },
};
</script>

<style scoped lang="scss">
@import "/@/components/gantt-v/css/gant-v.scss";

.ant-table-tbody > tr > td {
  border: 1px solid #ccc;
}

.ant-table-thead > tr > th {
  border: 1px solid #ccc;
  text-align: center;
  font-weight: 600;
}

.gantt-grab-no {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.contextmenu__item {
  display: block;
  line-height: 34px;
  text-align: center;
}

.contextmenu__item:not(:last-child) {
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}

.menu {
  position: absolute;
  background-color: #fff;
  width: 100px;
  /*height: 106px;*/
  font-size: 12px;
  color: #444040;
  border-radius: 4px;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  border-radius: 3px;
  border: 1px solid rgba(0, 0, 0, 0.15);
  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
  white-space: nowrap;
  z-index: 1000;
}

.contextmenu__item:hover {
  cursor: pointer;
  background: #66b1ff;
  border-color: #66b1ff;
  color: #fff;
}
</style>
